package com.lucenewapper;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.TermQuery;

import com.lucenewapper.beans.UserSearchable;
import com.lucenewapper.search.IndexHolder;
import com.lucenewapper.search.SearchHelper;
import com.lucenewapper.search.SearchResult;
import com.lucenewapper.search.Searchable;

public class LuceneSearchUtil {
	public final static int PAGE_SIZE = 30;
	public final static int DEFAULT_BATCH_COUNT = 2000;
	private final static Log log = LogFactory.getLog(LuceneSearchUtil.class);

	public final static String LUCENE_INDEX = "/data/lucenewapper/lucene_idx";
	private final static IndexHolder g_holder;

	public static final LuceneSearchUtil ME = new LuceneSearchUtil();
	static {
		try {
			File file = new File(LUCENE_INDEX);
			if (!file.exists()) {
				file.mkdirs();
			}
			g_holder = IndexHolder.init(LUCENE_INDEX);
		} catch (IOException e) {
			throw new RuntimeException("Unabled to init index repository.", e);
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<Long> findDatas(Class clazz, String key, String type, int page, int size) {
		// Sort sort = new Sort(new SortField("id", Type.LONG, true));//
		// 默认使用这个进行排序，ID降序
		BooleanQuery query = baseQuery();
		BooleanQuery subQuery = new DatasSearchQueryBuilder().key(key).type(type).build();
		query.add(subQuery, BooleanClause.Occur.MUST);
		List<Long> ids;
		try {
			ids = getHolder().find(clazz, query, null, null, page, size);
			return ids;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public int countDatas(Class clazz, String key, String type) {
		BooleanQuery query = baseQuery();
		BooleanQuery subQuery = new DatasSearchQueryBuilder().key(key).type(type).build();
		query.add(subQuery, BooleanClause.Occur.MUST);
		try {
			return getHolder().count(clazz, query, null);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return 0;
	}

	public SearchResult<UserSearchable> search(String key, String type, int page) {
		SearchResult<UserSearchable> result = new SearchResult<>();
		result.setTotalCount(countDatas(UserSearchable.class, key, type));
		List<Long> ids = findDatas(UserSearchable.class, key, type, page, PAGE_SIZE);
		if (ids != null && !ids.isEmpty()) {
			List<UserSearchable> recordsList = new ArrayList<>();
			for (long id : ids) {
				recordsList.add(UserSearchable.create(id));
			}
			result.setDatas(recordsList);
		}
		return result;
	}

	public long importDataById(String beanClass, long id) throws Exception {
		if (id <= 0) {
			return 0;
		}
		return buildById(beanClass, id);
	}

	/**
	 * 构建索引
	 * 
	 * @param beans
	 *            需要构建索引的对象，过个对象使用逗号连接
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public long buildById(String beans, long id)
			throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		long last_id = id;
		// 初始化索引管理器
		IndexHolder holder = IndexHolder.init(LUCENE_INDEX);
		for (String beanClass : StringUtils.split(beans, ',')) {
			Searchable obj = (Searchable) Class.forName(beanClass).newInstance();
			last_id = buildById(holder, obj, id);
			holder.optimize(obj.getClass());
		}
		return last_id;
	}

	/**
	 * 构建索引
	 */
	public long buildById(IndexHolder holder, Searchable obj, long id) throws IOException {
		int ic = 0;
		long last_id = id;
		do {
			List<? extends Searchable> objs = obj.ListAfter(last_id, DEFAULT_BATCH_COUNT);
			if (objs != null && objs.size() > 0) {
				ic += holder.add(objs);
				last_id = objs.get(objs.size() - 1).id();
				log.info(ic + " documents of " + obj.getClass().getSimpleName() + " added");
			}
			if (objs == null || objs.size() < DEFAULT_BATCH_COUNT) {
				break;
			}
		} while (true);
		log.info("all documents of " + obj.getClass().getSimpleName() + " added,last_id=" + last_id);
		return last_id;
	}

	private BooleanQuery baseQuery() {
		BooleanQuery baseQuery = new BooleanQuery();
		return baseQuery;
	}

	/**
	 * 清除所有索引
	 */
	public void clean_all() {
		File fileIndex = new File(LUCENE_INDEX);
		if (deleteDir(fileIndex)) {
			fileIndex.mkdir();
		} else {
			fileIndex.mkdir();
		}
	}

	/**
	 * 删除文件目录下的所有文件
	 * 
	 * @param file
	 *            要删除的文件目录
	 * @return 如果成功，返回true.
	 */
	private boolean deleteDir(File file) {
		if (file.isDirectory()) {
			File[] files = file.listFiles();
			for (int i = 0; i < files.length; i++) {
				deleteDir(files[i]);
			}
		}
		file.delete();
		return true;
	}

	public IndexHolder getHolder() {
		return g_holder;
	}
}

class DatasSearchQueryBuilder {
	private BooleanQuery filterQuery = new BooleanQuery();

	public DatasSearchQueryBuilder() {

	}

	public DatasSearchQueryBuilder key(String keyword) {
		if (StringUtils.isNotBlank(keyword)) {
			BooleanQuery query = new BooleanQuery();
			query.add(SearchHelper.makeQuery("name", keyword, 50f), BooleanClause.Occur.SHOULD);
			filterQuery.add(query, BooleanClause.Occur.MUST);
		}
		return this;
	}

	public DatasSearchQueryBuilder type(String type) {
		if (type != null && type.length() > 0) {
			BooleanQuery query = new BooleanQuery();
			query.add(new TermQuery(new Term("type", String.valueOf(type))), Occur.MUST);
			filterQuery.add(query, BooleanClause.Occur.MUST);
		}
		return this;
	}

	public BooleanQuery build() {
		return filterQuery;
	}
}